# HUB 插件 - 文件上传模块
# 实现SketchUp模型上传到HUB模型快递站功能

require 'sketchup.rb'
require 'net/http'
require 'uri'
require 'json'
require 'tempfile'
require 'fileutils'
# 使用Sketchup.require代替require_relative
Sketchup.require 'HUB/config'
Sketchup.require 'HUB/ui'
Sketchup.require 'HUB/jdt'  # 使用通用进度条模块

module HUB
  # 使用file_loaded?机制防止重复加载
  unless file_loaded?(__FILE__)
  # 缓存的远程配置
  @remote_config = nil
  @last_config_fetch = nil
  @config_cache_time = 300 # 缓存时间，单位秒
    
    # 跟踪最后一次上传的时间
    @last_upload_time = nil
    @upload_cooldown_seconds = 20  # 默认值，会被远程配置覆盖
  
  # 日志级别
  LOG_DEBUG = 1
  LOG_INFO = 2
  LOG_ERROR = 3
  @log_level = LOG_INFO # 默认日志级别
  
  # 日志记录
  def self.log(message, level = LOG_INFO)
    return if level < @log_level
    
    prefix = case level
      when LOG_DEBUG then "[DEBUG]"
      when LOG_INFO then "[INFO]"
      when LOG_ERROR then "[ERROR]"
      else "[LOG]"
    end
    
    puts "#{prefix} #{Time.now.strftime('%Y-%m-%d %H:%M:%S')} - #{message}"
  end
  
    # 标记此文件已加载
    file_loaded(__FILE__)
  end
  
  # 截断过长的名称
  def self.truncate_name(name, max_length = 25)
    return name if name.nil? || name.length <= max_length
    
    # 保留前面的部分，添加省略号
    truncated = name[0, max_length - 3] + "..."
    
    log("名称已截断: #{name} -> #{truncated}", LOG_DEBUG)
    return truncated
  end
  
  # 获取插件目录
  def self.get_plugin_dir
    # 获取当前文件路径
    current_file = __FILE__
    
    # 获取HUB目录的父目录（插件根目录）
    plugin_dir = File.dirname(File.dirname(current_file))
    
    log("插件目录: #{plugin_dir}", LOG_DEBUG)
    return plugin_dir
  end
  
  # 确保临时目录存在
  def self.ensure_temp_dir
    plugin_dir = get_plugin_dir
    temp_dir = File.join(plugin_dir, 'HUB', 'Temp')
    
    # 如果临时目录不存在，创建它
    unless File.directory?(temp_dir)
      begin
        FileUtils.mkdir_p(temp_dir)
        log("创建临时目录: #{temp_dir}")
      rescue => e
        log("创建临时目录失败: #{e.message}", LOG_ERROR)
        # 如果创建失败，使用系统临时目录作为备选
        return Dir.tmpdir
      end
    end
    
    log("使用临时目录: #{temp_dir}", LOG_DEBUG)
    return temp_dir
  end
  
  # 清空临时目录
  def self.clean_temp_dir
    temp_dir = ensure_temp_dir
    
    begin
      # 查找目录中的所有文件
      files = Dir.glob(File.join(temp_dir, "*"))
      
      if files.empty?
        log("临时目录为空，无需清理", LOG_DEBUG)
        return true
      end
      
      log("清理临时目录，发现 #{files.length} 个文件")
      
      # 删除所有文件
      files.each do |file|
        if File.file?(file)
          File.delete(file)
          log("删除临时文件: #{File.basename(file)}", LOG_DEBUG)
        end
      end
      
      log("临时目录清理完成")
      return true
    rescue => e
      log("清理临时目录时出错: #{e.message}", LOG_ERROR)
      return false
    end
  end
  
  # 上传模型到HUB模型快递站
  def self.upload_model_impl
    begin
      log("开始上传模型流程")
      
      # 获取远程配置
      remote_config = get_remote_config
      if remote_config.nil?
        UIHelper.show_error("无法获取远程配置，请检查网络连接后重试。")
        log("无法获取远程配置", LOG_ERROR)
        return
      end
      
      # 检查上传冷却时间
      if @last_upload_time && (Time.now - @last_upload_time) < @upload_cooldown_seconds
        remaining = @upload_cooldown_seconds - (Time.now - @last_upload_time).to_i
        message = "请稍等 #{remaining} 秒后再尝试上传"
        log(message, LOG_INFO)
        UIHelper.show_status_text(message)
        UIHelper.show_error("上传功能正在冷却中，请稍等 #{remaining} 秒")
        return
      end
      
      # 检查上传限制数量
      upload_limit = remote_config[:upload_limit]
      log("远程配置: 上传限制=#{upload_limit}，过期时间=#{remote_config[:expire_value]} #{remote_config[:expire_style]}，文件大小限制=#{remote_config[:file_size_limit]}MB，连接超时=#{remote_config[:connection_timeout]}秒，上传间隔=#{@upload_cooldown_seconds}秒")
      
      if upload_limit <= 0
        UIHelper.show_error("当前不允许上传，请稍后再试。")
        log("上传被限制，当前限制值: #{upload_limit}", LOG_ERROR)
        return
      end
      
      # 获取当前模型和选择
      model = Sketchup.active_model
      selection = model.selection
      
      # 检查是否有选择
      if selection.empty?
        UIHelper.show_error("请先选择要上传的组或组件，或整个模型。")
        log("未选择任何对象", LOG_ERROR)
        return
      end
      
      # 处理选择，提取组和组件
      selected_entities = []
      selection.each do |entity|
        if entity.is_a?(Sketchup::Group) || entity.is_a?(Sketchup::ComponentInstance)
          selected_entities << entity
        end
      end
      
      # 如果没有选中组或组件，提示用户
      if selected_entities.empty?
        UIHelper.show_error("请选择组或组件进行上传。")
        log("未选择任何组或组件", LOG_ERROR)
        return
      end
      
      log("选中的对象数量: #{selected_entities.length}")
      
      # 清空临时目录
      clean_temp_dir
      
      # 显示状态消息
      UIHelper.show_status_text("准备处理选中对象...")
      
      begin
        # 处理选择的对象
        if selected_entities.length == 1
          # 单个对象，直接处理
          entity = selected_entities.first
          log("处理单个对象: #{entity.typename} - #{get_entity_name(entity)}")
          temp_file = save_selection_to_temp(entity)
          entity_name = get_entity_name(entity)
        else
          # 多个对象，先创建一个组，然后处理
          log("处理多个对象 (#{selected_entities.length}个)，先创建一个组")
          
          # 开始操作
          model.start_operation('创建多对象上传组', true)
          
          # 创建组名
          current_time = Time.now.strftime("%Y%m%d_%H%M%S")
          group_name = "HUB上传组_#{current_time}"
          
          # 创建一个组包含所有选中对象
          begin
            # 保存当前选择，因为创建组后会清空选择
            original_selection = selected_entities.dup
            
            # 使用用户提供的API代码创建组
            log("创建组: #{group_name}")
            group = model.active_entities.add_group(selected_entities)
            group.name = group_name
            
            # 选择新创建的组
            model.selection.clear
            model.selection.add(group)
            
            log("成功创建组并选中: #{group_name}")
            
            # 保存组到临时文件
            temp_file = save_selection_to_temp(group)
            entity_name = group_name
            
            # 提交操作
            model.commit_operation
          rescue => e
            # 操作失败，回滚
            model.abort_operation
            log("创建组失败: #{e.message}", LOG_ERROR)
            
            # 恢复原始选择
            model.selection.clear
            original_selection.each { |entity| model.selection.add(entity) }
            
            # 使用原始方法作为备选
            log("使用备选方法保存多个对象")
          temp_file = save_multiple_to_temp(selected_entities)
            entity_name = "多个对象"
          end
        end
        
        if temp_file.nil?
          UIHelper.show_error("保存选中对象到临时文件失败。")
          log("保存选中对象到临时文件失败", LOG_ERROR)
          return
        end
        
        log("临时文件已创建: #{temp_file}, 大小: #{File.size(temp_file)} 字节")
        
        # 获取文件大小
        file_size = File.size(temp_file)
        file_size_mb = (file_size / 1024.0 / 1024.0).round(2)
        
        # 检查文件大小是否超过限制
        file_size_limit = remote_config[:file_size_limit]
        if file_size_mb > file_size_limit
          UIHelper.show_error("文件大小超过限制！\n当前文件: #{file_size_mb}MB\n上传限制: #{file_size_limit}MB")
          log("文件大小超过限制: #{file_size_mb}MB > #{file_size_limit}MB", LOG_ERROR)
          return
        end
        
        # 获取过期时间设置
        expire_value = remote_config[:expire_value]
        expire_style = remote_config[:expire_style]
        
        # 显示状态信息
        UIHelper.show_status_text("正在上传 #{entity_name}... (#{file_size_mb} MB)")
        
        # 上传文件到HUB模型快递站
        log("开始上传文件 #{entity_name} 到HUB模型快递站，大小: #{file_size_mb} MB")
        code = upload_file_to_codebox(temp_file, expire_value, expire_style)
        
        if code
          # 设置最后上传时间
          @last_upload_time = Time.now
          
          # 构建带前缀的提取码
          truncated_name = truncate_name(entity_name)
          prefixed_code = "【#{truncated_name}】模型提取码:#{code}"
          
          log("上传成功，提取码: #{code}")
          
          # 复制完整格式到剪贴板
          self.copy_to_clipboard(prefixed_code)
          
          # 显示提取码
          UIHelper.show_message("上传成功！\n#{prefixed_code}")
          
          UIHelper.show_status_text("提取码 #{code} 已复制到剪贴板")
          log("提取码已复制到剪贴板")
        else
          UIHelper.show_error("上传失败，请稍后重试。")
          log("上传失败，未获取到提取码", LOG_ERROR)
        end
      ensure
        # 删除临时文件
        begin
          if temp_file && File.exist?(temp_file)
            File.delete(temp_file)
            log("临时文件已删除: #{temp_file}")
          end
        rescue => e
          log("删除临时文件时出错: #{e.message}", LOG_ERROR)
        end
      end
    rescue => e
      UIHelper.show_error("上传过程中发生错误: #{e.message}")
      log("上传过程中发生错误: #{e.message}\n#{e.backtrace.join("\n")}", LOG_ERROR)
      UIHelper.show_status_text("")
    end
  end
  
  # 获取远程配置
  def self.get_remote_config
    current_time = Time.now
    
    # 检查缓存是否过期
    if !@remote_config.nil? && !@last_config_fetch.nil? && 
       (current_time - @last_config_fetch) < @config_cache_time
      log("使用缓存的远程配置", LOG_DEBUG)
      return @remote_config
    end
    
    log("获取远程配置", LOG_DEBUG)
    
    begin
      # 构建请求URL
      uri = URI.parse("http://api.cdsjcc.cn/hub/ss1.txt")
      
      # 发送GET请求
      log("发送请求到: #{uri}", LOG_DEBUG)
      
      # 设置超时
      http = Net::HTTP.new(uri.host, uri.port)
      http.open_timeout = 10
      http.read_timeout = 10
      
      response = http.request(Net::HTTP::Get.new(uri.request_uri))
      
      if response.code == "200"
        content = response.body.force_encoding('UTF-8')
        log("收到响应内容: #{content}", LOG_DEBUG)
        
        # 解析配置参数
        upload_limit = parse_config_param(content, 'sl')
        expire_value = parse_config_param(content, 'gq')
        expire_style = parse_config_param(content, 'gqdw', true)
        file_size_limit = parse_config_param(content, 'wjdx', false, 500) # 默认500MB作为应急值
        connection_timeout = parse_config_param(content, 'ljcs', false, 300) # 默认300秒作为应急值
        upload_cooldown = parse_config_param(content, 'scjg', false, 20) # 默认20秒作为应急值
        
        # 验证参数
        upload_limit = upload_limit.to_i
        expire_value = expire_value.to_i
        expire_style = expire_style.to_s
        file_size_limit = file_size_limit.to_i
        connection_timeout = connection_timeout.to_i
        upload_cooldown = upload_cooldown.to_i
        
        # 确保expire_style有效
        unless ['day', 'hour', 'minute'].include?(expire_style)
          log("无效的过期时间单位: #{expire_style}，使用默认值: day", LOG_DEBUG)
          expire_style = 'day'
        end
        
        # 确保文件大小限制有效
        if file_size_limit <= 0
          log("无效的文件大小限制: #{file_size_limit}，使用默认值: 500", LOG_DEBUG)
          file_size_limit = 500
        end
        
        # 确保连接超时有效
        if connection_timeout <= 0
          log("无效的连接超时: #{connection_timeout}，使用默认值: 300", LOG_DEBUG)
          connection_timeout = 300
        end
        
        # 确保上传冷却时间有效
        if upload_cooldown <= 0
          log("无效的上传冷却时间: #{upload_cooldown}，使用默认值: 20", LOG_DEBUG)
          upload_cooldown = 20
        end
        
        # 设置上传冷却时间
        @upload_cooldown_seconds = upload_cooldown
        log("上传冷却时间设置为: #{@upload_cooldown_seconds}秒")
        
        # 更新缓存
        @remote_config = {
          upload_limit: upload_limit,
          expire_value: expire_value,
          expire_style: expire_style,
          file_size_limit: file_size_limit,
          connection_timeout: connection_timeout
        }
        @last_config_fetch = current_time
        
        log("远程配置已更新: 上传限制=#{upload_limit}, 过期时间=#{expire_value}, 过期单位=#{expire_style}, 文件大小限制=#{file_size_limit}MB, 连接超时=#{connection_timeout}秒, 上传间隔=#{@upload_cooldown_seconds}秒")
        return @remote_config
      else
        log("获取远程配置失败，状态码: #{response.code}", LOG_ERROR)
      end
    rescue => e
      log("获取远程配置时出错: #{e.message}", LOG_ERROR)
    end
    
    # 返回默认配置
    log("使用默认配置", LOG_INFO)
    {
      upload_limit: 1,
      expire_value: 1,
      expire_style: 'day',
      file_size_limit: 50, # 默认50MB作为应急值
      connection_timeout: 300 # 默认300秒作为应急值
    }
  end
  
  # 解析配置参数
  def self.parse_config_param(content, param_name, is_string = false, default_value = 1)
    # 匹配格式如 [sl]="1" 的参数
    match = content.match(/\[#{param_name}\]="([^"]*)"/)
    
    if match && match[1]
      value = is_string ? match[1] : match[1].to_i
      log("解析配置参数 #{param_name}=\"#{match[1]}\"", LOG_DEBUG)
      return value
    end
    
    # 返回默认值
    log("配置参数 #{param_name} 未找到，使用默认值: #{default_value}", LOG_DEBUG)
    default_value
  end
  
  # 获取实体名称（用于显示和提取码前缀）
  def self.get_entity_name(entity, is_multiple = false)
    if is_multiple
      return "多个对象"
    end
    
    if entity.is_a?(Sketchup::Group)
      return entity.name.empty? ? "未命名组" : entity.name
    elsif entity.is_a?(Sketchup::ComponentInstance)
      return entity.name.empty? ? (entity.definition.name.empty? ? "未命名组件" : entity.definition.name) : entity.name
    else
      return "未命名对象"
    end
  end
  
  # 保存选中的组或组件到临时文件
  def self.save_selection_to_temp(entity)
    begin
      # 使用插件目录下的Temp文件夹
      temp_dir = ensure_temp_dir
      temp_file = File.join(temp_dir, "sketchup_selection_#{Time.now.to_i}.skp")
      log("开始保存选中对象到临时文件: #{temp_file}")
      
      if entity.is_a?(Sketchup::Group)
        # 使用组的定义直接保存，避免转换为组件
        model = Sketchup.active_model
        model.start_operation('保存组到临时文件', true)
        log("保存组定义")
        component_definition = entity.definition
        status = component_definition.save_as(temp_file)
        model.commit_operation
      elsif entity.is_a?(Sketchup::ComponentInstance)
        # 直接保存组件定义
        log("保存组件定义")
        status = entity.definition.save_as(temp_file)
      end
      
      if status
        log("选中对象已保存到临时文件")
        return temp_file
      else
        log("保存选中对象失败", LOG_ERROR)
        return nil
      end
    rescue => e
      model.abort_operation if defined?(model) && model && model.respond_to?(:abort_operation)
      log("保存选中对象到临时文件时出错: #{e.message}", LOG_ERROR)
      return nil
    end
  end
  
  # 保存多个选中对象到临时文件（合并为一个组）
  def self.save_multiple_to_temp(entities)
    begin
      model = Sketchup.active_model
      
      # 创建一个临时组
      model.start_operation('合并多个对象为组', true)
      log("创建临时组以合并多个对象")
      temp_group = model.active_entities.add_group
      
      # 复制所有选中的实体到这个组
      entities.each_with_index do |entity, index|
        if entity.is_a?(Sketchup::Group)
          # 复制组的内容到临时组
          log("正在处理组 #{index+1}/#{entities.length}: #{get_entity_name(entity)}")
          entity_copy = temp_group.entities.add_instance(entity.definition, entity.transformation)
          entity_copy.make_unique # 确保不影响原始组
        elsif entity.is_a?(Sketchup::ComponentInstance)
          # 复制组件实例到临时组
          log("正在处理组件 #{index+1}/#{entities.length}: #{get_entity_name(entity)}")
          temp_group.entities.add_instance(entity.definition, entity.transformation)
        end
      end
      
      # 保存临时组到文件（使用插件目录下的Temp文件夹）
      temp_dir = ensure_temp_dir
      temp_file = File.join(temp_dir, "sketchup_multiple_#{Time.now.to_i}.skp")
      log("保存合并后的临时组到文件: #{temp_file}")
      status = temp_group.definition.save_as(temp_file)
      
      # 删除临时组
      temp_group.erase!
      model.commit_operation
      log("临时组已删除，操作已完成")
      
      if status
        log("合并后的组已保存到临时文件")
        return temp_file
      else
        log("保存合并后的组失败", LOG_ERROR)
        return nil
      end
    rescue => e
      model.abort_operation if model && model.respond_to?(:abort_operation)
      log("保存多个对象到临时文件时出错: #{e.message}\n#{e.backtrace.join("\n")}", LOG_ERROR)
      return nil
    end
  end
  
  # 上传文件到HUB模型快递站
  def self.upload_file_to_codebox(file_path, expire_value, expire_style)
    # 设置边界字符串
    boundary = "AaB03x#{rand(10000000)}"
    
    begin
      # 读取文件内容
      file_content = File.binread(file_path)
      file_size = file_content.bytesize
      file_size_mb = (file_size / 1024.0 / 1024.0).round(2)
      
      log("准备上传文件: #{file_path}, 大小: #{file_size_mb} MB")
      
      # 获取超时设置
      remote_config = get_remote_config
      connection_timeout = remote_config[:connection_timeout]
      log("使用连接超时: #{connection_timeout}秒")
      
      # 创建进度显示 - 使用新的通用进度条模块
      progress_id = JDT.create_upload_progress("上传", File.basename(file_path))
      JDT.update_progress(progress_id, 10, "正在准备上传...")
      
      # 准备HTTP请求
      uri = URI.parse(API_URL)
      log("快递站运行正常")
      
      http = Net::HTTP.new(uri.host, uri.port)
      
      # 设置超时
      http.open_timeout = 30
      http.read_timeout = connection_timeout
      
      # 创建POST请求
      request = Net::HTTP::Post.new(uri.request_uri)
      
      # 构建multipart/form-data请求体
      post_body = []
      
      # 添加过期时间参数
      log("设置过期时间: #{expire_value} #{expire_style}")
      post_body << "--#{boundary}\r\n"
      post_body << "Content-Disposition: form-data; name=\"expire_value\"\r\n\r\n"
      post_body << "#{expire_value}\r\n"
      
      post_body << "--#{boundary}\r\n"
      post_body << "Content-Disposition: form-data; name=\"expire_style\"\r\n\r\n"
      post_body << "#{expire_style}\r\n"
      
      # 添加文件数据头
      post_body << "--#{boundary}\r\n"
      post_body << "Content-Disposition: form-data; name=\"file\"; filename=\"#{File.basename(file_path)}\"\r\n"
      post_body << "Content-Type: application/octet-stream\r\n\r\n"
      
      # 更新状态栏和进度
      UIHelper.show_status_text("准备发送 #{file_size_mb} MB 数据...")
      JDT.update_progress(progress_id, 20, "准备发送数据...")
      
      # 合并请求体
      request_body_start = post_body.join
      request_body_end = "\r\n--#{boundary}--\r\n"
      
      # 计算总长度
      content_length = request_body_start.bytesize + file_content.bytesize + request_body_end.bytesize
      
      # 设置请求头
      request["Content-Type"] = "multipart/form-data; boundary=#{boundary}"
      request["Content-Length"] = content_length.to_s
      
      # 设置请求体
      request.body = request_body_start + file_content + request_body_end
      
      # 更新状态栏和进度
      UIHelper.show_status_text("正在上传文件，请稍候...")
      JDT.update_progress(progress_id, 30, "正在上传文件...")
      
      # 发送请求
      log("发送HTTP请求")
      response = http.request(request)
      
      # 模拟进度增加，让用户感知到文件正在处理
      for i in 35..85
        # 每隔5%更新一次进度
        if i % 5 == 0
          sleep(0.1) # 短暂延迟制造渐进效果
          JDT.update_progress(progress_id, i, "正在上传文件...")
        end
      end
      
      # 更新状态栏和进度
      UIHelper.show_status_text("正在处理响应...")
      JDT.update_progress(progress_id, 90, "文件已上传，正在处理响应...")
      
      # 解析响应
      log("收到响应，状态码: #{response.code}")
      
      if response.code == "200"
        begin
          result = JSON.parse(response.body)
          if result["code"] == 200 && result["detail"] && result["detail"]["code"]
            # 更新状态栏和进度
            UIHelper.show_status_text("上传完成，准备复制提取码...")
            
            # 先显示99%进度，制造完成感
            JDT.update_progress(progress_id, 99, "上传已完成！正在生成提取码...")
            
            # 稍微暂停
            sleep(0.3)
            
            # 确保进度到达100%
            JDT.complete_progress(progress_id, "上传已完成！")
            
            # 给用户时间看到完成消息，然后明确隐藏进度显示
            sleep(2)
            JDT.hide_progress(progress_id)
            
            log("上传成功，提取码: #{result["detail"]["code"]}")
            return result["detail"]["code"]
          else
            JDT.update_progress(progress_id, 100, "上传失败: #{result["msg"] || '未知错误'}")
            sleep(2)
            JDT.hide_progress(progress_id)
            log("API返回错误: #{result["msg"] || '未知错误'}", LOG_ERROR)
          end
        rescue JSON::ParserError => e
          JDT.update_progress(progress_id, 100, "解析响应失败")
          sleep(2)
          JDT.hide_progress(progress_id)
          log("解析API响应时出错: #{e.message}\n响应内容: #{response.body[0..200]}", LOG_ERROR)
        end
      else
        JDT.update_progress(progress_id, 100, "上传失败，状态码: #{response.code}")
        sleep(2)
        JDT.hide_progress(progress_id)
        log("上传请求失败，状态码: #{response.code}", LOG_ERROR)
        
        # 尝试记录响应内容，帮助调试
        begin
          log("响应内容: #{response.body[0..200]}...", LOG_ERROR)
        rescue => e
          log("无法记录响应内容: #{e.message}", LOG_ERROR)
        end
      end
    rescue => e
      if defined?(progress_id) && progress_id
        JDT.update_progress(progress_id, 100, "上传出错: #{e.message}")
        sleep(2)
        JDT.hide_progress(progress_id)
      end
      log("上传文件过程中发生错误: #{e.message}\n#{e.backtrace.join("\n")}", LOG_ERROR)
    end
    
    return nil
  end
  
  # 复制文本到剪贴板
  def self.copy_to_clipboard(text)
    log("尝试复制文本到剪贴板: #{text}")
    
    # 这个功能在不同平台实现方式不同
    # 在Windows上使用win32ole库隐藏命令窗口
    if RUBY_PLATFORM =~ /mswin|mingw|windows/
      begin
        require 'win32ole'
        # 创建WScript.Shell对象
        shell = WIN32OLE.new('WScript.Shell')
        # 使用Run方法运行命令，参数0表示隐藏窗口，参数1表示等待命令完成
        shell.Run("cmd /c echo #{text} | clip", 0, 1)
        log("复制到剪贴板")
      rescue LoadError
        # 如果win32ole库不可用，则使用system命令
        system("echo #{text} | clip")
        log("使用system命令复制到剪贴板")
      end
    # 在macOS上可以使用以下方式
    elsif RUBY_PLATFORM =~ /darwin/
      system("echo '#{text}' | pbcopy")
      log("使用pbcopy复制到剪贴板")
    else
      log("不支持的平台，无法复制到剪贴板", LOG_ERROR)
    end
  end

end # module HUB
